/************************************************************************

								Heroes III
					  Copyright 1999, The 3DO Company

	------------------------------------------------------------------
  						 copy_on_write_ptr.h

	$Header: /Heroes4/copy_on_write_ptr.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( COPY_ON_WRITE_PTR_H_INCLUDED )
#define COPY_ON_WRITE_PTR_H_INCLUDED

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#include <algorithm>
#include <memory>

#pragma warning( push )
#pragma warning( disable: 4786 ) // Disable the ever so helpful "identifier was truncated to '255' characters" warning

/////////////////////////////////////////////////////////////////////////////
// Details namespace

namespace copy_on_write_ptr_details
{

	/////////////////////////////////////////////////////////////////////////////
	// t_body class template - auxillary type used by t_copy_on_write_ptr class
	// template

	template < typename t_elem >
	class t_body
	{
	public:
		// Data members
		unsigned	count;
		t_elem		elem;

		// Constructors
		t_body()
			:	count( 1 ),
				elem()
		{
		}

		t_body( t_elem const & initial_value )
			:	count( 1 ),
				elem( initial_value )
		{
		}
	};

	/////////////////////////////////////////////////////////////////////////////
	// t_default_body_ref class template

	template < typename t_elem >
	class t_default_body_ref
	{
	public:
		// Data members
		t_body< t_elem > *	m_body_ptr;

		// Destructor
		~t_default_body_ref();
	};

	/////////////////////////////////////////////////////////////////////////////
	// t_default_body_ref members

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_elem >
	inline t_default_body_ref< t_elem >::~t_default_body_ref()
	{
		if ( m_body_ptr != 0 && --m_body_ptr->count == 0 )
		{
			delete m_body_ptr;
			m_body_ptr = 0;
		}
	}

} // copy_on_write_ptr_details namespace

/////////////////////////////////////////////////////////////////////////////
// t_copy_on_write_ptr class template - Each instance of a
// t_copy_on_write_ptr 'logically' owns and points to its own instance of its
// element type.  However, t_copy_on_write_ptr's implement lazy copying.
// Copying of the element is deferred until a non-const access forces a
// unique copy.

template < typename t_elem >
class t_copy_on_write_ptr
{
public:
	// Types
	typedef t_elem element_type;

	// Constructors/Destructor
	t_copy_on_write_ptr();
	explicit t_copy_on_write_ptr( t_elem const & initial_value );
	t_copy_on_write_ptr( t_copy_on_write_ptr const & other );
	~t_copy_on_write_ptr();

	// Member functions
	t_elem *		get();
	t_elem const *	get() const { return &m_body_ptr->elem; }
	t_elem const *	get_const() const { return get(); }

	void			reset();
	void			reset( t_elem const & new_value );

	void			swap( t_copy_on_write_ptr & other );

	// Operators
	t_copy_on_write_ptr &	operator=( t_copy_on_write_ptr const & other );

	t_elem &				operator*() { return *get(); }
	t_elem const &			operator*() const { return *get(); }
	t_elem *				operator->() { return get(); }
	t_elem const *			operator->() const { return get(); }

private:
	// Types
	typedef copy_on_write_ptr_details::t_body< t_elem >				t_body;
	typedef copy_on_write_ptr_details::t_default_body_ref< t_elem >	t_default_body_ref;

	// Static data
	static t_default_body_ref	g_default_body_ref;

	// Static member functions
	static t_body * get_default_body();

	// Data members
	t_body *	m_body_ptr;

	// Member functions
	void split();
};

/////////////////////////////////////////////////////////////////////////////
// t_copy_on_write_ptr members

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem >
copy_on_write_ptr_details::t_default_body_ref< t_elem >
t_copy_on_write_ptr< t_elem >::g_default_body_ref;

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem >
inline typename t_copy_on_write_ptr< t_elem >::t_body *
t_copy_on_write_ptr< t_elem >::get_default_body()
{
	if ( g_default_body_ref.m_body_ptr == 0 )
		g_default_body_ref.m_body_ptr = new t_body;
	return g_default_body_ref.m_body_ptr;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem >
inline t_copy_on_write_ptr< t_elem >::t_copy_on_write_ptr()
	:	m_body_ptr( get_default_body() )
{
	++m_body_ptr->count;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem >
inline t_copy_on_write_ptr< t_elem >::t_copy_on_write_ptr(
	t_elem const & initial_value )
	:	m_body_ptr( new t_body( initial_value ) )
{
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem >
inline t_copy_on_write_ptr< t_elem >::t_copy_on_write_ptr(
	t_copy_on_write_ptr const & other )
	:	m_body_ptr( other.m_body_ptr )
{
	++m_body_ptr->count;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem >
inline t_copy_on_write_ptr< t_elem >::~t_copy_on_write_ptr()
{
	if ( --m_body_ptr->count == 0 )
		delete m_body_ptr;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem >
inline t_copy_on_write_ptr< t_elem > &
t_copy_on_write_ptr< t_elem >::operator=( t_copy_on_write_ptr const & other )
{
	++other.m_body_ptr->count;
	if ( --m_body_ptr->count == 0 )
		delete m_body_ptr;
	m_body_ptr = other.m_body_ptr;
	return *this;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem >
inline t_elem * t_copy_on_write_ptr< t_elem >::get()
{
	if ( m_body_ptr->count > 1 )
		split();
	return &m_body_ptr->elem;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem >
void t_copy_on_write_ptr< t_elem >::reset()
{
	t_body * new_body_ptr = get_default_body();
	++new_body_ptr->count;
	if ( --m_body_ptr->count == 0 )
		delete m_body_ptr;
	m_body_ptr = new_body_ptr;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem >
void t_copy_on_write_ptr< t_elem >::reset( t_elem const & new_value )
{
	if ( m_body_ptr->count > 1 )
	{
		t_body * new_body_ptr = new t_body( new_value );
		--m_body_ptr->count;
		m_body_ptr = new_body_ptr;
	}
	else
		m_body_ptr->elem = new_value;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem >
inline void t_copy_on_write_ptr< t_elem >::swap( t_copy_on_write_ptr & other )
{
	std::swap( m_body_ptr, other.m_body_ptr );
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem >
void t_copy_on_write_ptr< t_elem >::split()
{
	t_body * new_body_ptr = new t_body( m_body_ptr->elem );
	--m_body_ptr->count;
	m_body_ptr = new_body_ptr;
}

#pragma warning( pop )

#endif // !defined( COPY_ON_WRITE_PTR_H_INCLUDED )
